package cool.taomu.software.fig.asm.test

import cool.taomu.software.fig.classloader.FigClassLoader
import java.io.IOException
import java.io.InputStream
import java.io.PrintWriter
import java.lang.invoke.CallSite
import java.lang.invoke.ConstantCallSite
import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import org.junit.Test
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Handle
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.objectweb.asm.util.TraceClassVisitor

class ConvertToInvokeDynamic extends MethodVisitor {
    Handle bsm;

    new(MethodVisitor target, Class<?> linkerClass) {
        super(Opcodes.ASM5, target);
        var mt = MethodType.methodType(CallSite, MethodHandles.Lookup, String, MethodType);
        bsm = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(linkerClass), "bootstrap",
            mt.toMethodDescriptorString(), false);
    }

    def static byte[] convertInvokeVirtual(InputStream in, Class<?> linkerClass) throws IOException {
        var ClassReader cr = new ClassReader(in);

        val classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
        val classVisitor = new TraceClassVisitor(classWriter, new PrintWriter(System.err));

        cr.accept(new ClassVisitor(Opcodes.ASM5, classVisitor) {
            override MethodVisitor visitMethod(int access, String name, String desc, String signature,
                String[] exceptions) {
                return new ConvertToInvokeDynamic(super.visitMethod(access, name, desc, signature, exceptions),
                    linkerClass);
            }
        }, 0);
        return classWriter.toByteArray();
    }

    override void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
        var desc2 = desc;
        if (opcode == Opcodes.INVOKEVIRTUAL) {
            desc2 = '(' + (owner.charAt(0) != '[' ? 'L' + owner + ';' : owner) + desc.substring(1);
            super.visitInvokeDynamicInsn(name, desc2, bsm);
        } else {
            super.visitMethodInsn(opcode, owner, name, desc, itf);

        }
    }
}
class LinkerClass{
     def static CallSite bootstrap(MethodHandles.Lookup caller, String name,
        MethodType type) throws NoSuchMethodException, IllegalAccessException {
        /*   MethodHandles.Lookup lookup = MethodHandles.lookup();
         Class<?> thisClass = lookup.lookupClass();*/
        var MethodHandle sayHello = caller.findVirtual(type.parameterType(0), name, type);
        return new ConstantCallSite(sayHello);
    }
}
class CallSiteTest {

    def void a() {
        println("Hello World!!!");
    }

    //@Test
    def void t() {
        try(var InputStream is = CallSiteTest.getResourceAsStream("CallSiteTest.class")) {
            var code = ConvertToInvokeDynamic.convertInvokeVirtual(is,LinkerClass);
            var cl = new FigClassLoader();
            var transformed = cl.register("CallSiteTest",code);
            transformed.getMethod("example").invoke(null); 
        }
    }

    def static void example() {
        System.out.println(Runtime.getRuntime().freeMemory()+" bytes free");
    }
}
